package service

import (
	"context"
	baseStore "gitee.com/zaiqiang231/go-base-app/base_app/store"
	postProto "gitee.com/zaiqiang231/zcamp-service-camp/app/rpc/proto"
	"gitee.com/zaiqiang231/zcamp-service-user/app/constants"
	"gitee.com/zaiqiang231/zcamp-service-user/app/model"
	"gitee.com/zaiqiang231/zcamp-service-user/app/model/req"
	remoteRpc "gitee.com/zaiqiang231/zcamp-service-user/app/rpc/remote"
	"gitee.com/zaiqiang231/zcamp-service-user/app/store"
	"gitee.com/zaiqiang231/zcamp-service-user/app/util"
	"github.com/pkg/errors"
	"gorm.io/gorm"
	"sync"
)

type ServiceUser struct{}

var ServiceUserInstance = new(ServiceUser)

func (service *ServiceUser) UserCreate(ctx context.Context, request req.UserCreateReq) (err error) {
	baseStore.GetDataStore().Execute(func(db *gorm.DB) {
		err = db.Transaction(func(tx *gorm.DB) error {
			var tmperr error
			request.User.Uid, tmperr = store.IdGetInMQ(ctx)
			if tmperr != nil {
				return tmperr
			}
			tmperr = tx.Create(&request.User).Error
			if tmperr != nil {
				return tmperr
			}
			return nil
		})
	})
	return
}

func (service *ServiceUser) UserUpdateForMap(info map[string]interface{}) (err error) {
	baseStore.GetDataStore().Execute(func(db *gorm.DB) {
		err = db.Transaction(func(tx *gorm.DB) error {
			var old model.User
			var txErr error
			if errors.Is(tx.Where("uid = ?", info["uid"]).First(&old).Error, gorm.ErrRecordNotFound) {
				txErr = errors.New("原记录不存在")
			}
			if txErr != nil {
				return txErr
			}

			delete(info, "updated_at")
			txErr = tx.Model(&old).Omit("id", "uid", "created_at", "deleted_at").Updates(info).Error
			if txErr != nil {
				return txErr
			}

			return nil
		})

	})
	return err
}

func (service *ServiceUser) DeleteBannerList(info req.IdsReq) (err error) {
	baseStore.GetDataStore().Execute(func(db *gorm.DB) {
		var tmpList []model.User
		err = db.Model(&model.User{}).Where("uid in ?", info.Ids).Find(&tmpList).Delete(&tmpList).Error
	})
	return
}

func (service *ServiceUser) GetUserList(info req.UserListReq) (list interface{}, total int64, err error) {
	baseStore.GetDataStore().Execute(func(db *gorm.DB) {
		limit := info.PageSize
		offset := info.PageSize * (info.Page - 1)
		var tempDb = db
		if info.Uid != 0 {
			tempDb = tempDb.Where("uid = ?", info.Uid)
		}
		if info.NickName != "" {
			tempDb = tempDb.Where("nick_name LIKE ?", "%"+info.NickName+"%")
		}
		if info.Enable != 0 {
			tempDb = tempDb.Where("enable = ?", info.Enable)
		}
		var tmpList []model.User
		err = tempDb.Model(&model.User{}).Count(&total).Error
		if err != nil {
			return
		}
		err = tempDb.Model(&model.User{}).Offset(offset).Limit(limit).Find(&tmpList).Error
		if err != nil {
			return
		}
		list = tmpList
	})
	return
}

func (service *ServiceUser) Follow(uid, targetUid int64) (relation string, err error) {
	baseStore.GetDataStore().Execute(func(db *gorm.DB) {
		err = db.Transaction(func(tx *gorm.DB) error {
			var old model.Follow
			var txErr error
			if errors.Is(tx.Where("uid = ?", uid).Where("follow_uid = ?", targetUid).First(&old).Error, gorm.ErrRecordNotFound) {
				//原记录不存在
				var follow = model.Follow{
					Uid:       uid,
					FollowUid: targetUid,
				}
				txErr = tx.Create(&follow).Error
				if txErr != nil {
					return txErr
				}
			}
			var oldFans model.Fans
			if errors.Is(tx.Where("uid = ?", targetUid).Where("fans_uid = ?", uid).First(&oldFans).Error, gorm.ErrRecordNotFound) {
				var fans = model.Fans{
					Uid:     targetUid,
					FansUid: uid,
				}
				txErr = tx.Create(&fans).Error
				if txErr != nil {
					return txErr
				}
			}

			if errors.Is(tx.Where("uid = ?", targetUid).Where("follow_uid = ?", uid).First(&old).Error, gorm.ErrRecordNotFound) {
				relation = constants.FOLLOWING
			} else {
				relation = constants.MUTIL
			}

			return nil
		})
	})
	return
}

func (service *ServiceUser) UnFollow(uid, targetUid int64) (relation string, err error) {
	baseStore.GetDataStore().Execute(func(db *gorm.DB) {
		err = db.Transaction(func(tx *gorm.DB) error {
			var tmpFollow model.Follow
			var txErr error
			txErr = tx.Model(&model.Follow{}).Where("uid = ?", uid).Where("follow_uid = ?", targetUid).Delete(&tmpFollow).Error
			if txErr != nil {
				return txErr
			}
			var tmpFans model.Fans
			txErr = tx.Model(&model.Fans{}).Where("uid = ?", targetUid).Where("fans_uid = ?", uid).Delete(&tmpFans).Error
			if txErr != nil {
				return txErr
			}

			if errors.Is(tx.Where("uid = ?", targetUid).Where("follow_uid = ?", uid).First(&tmpFollow).Error, gorm.ErrRecordNotFound) {
				relation = constants.NO_RELATION
			} else {
				relation = constants.BE_FOLLOWED
			}

			return nil
		})
	})
	return
}

func (service ServiceUser) GetFollowList(uid int64) (list []model.Follow, err error) {
	baseStore.GetDataStore().Execute(func(db *gorm.DB) {
		err = db.Transaction(func(tx *gorm.DB) error {
			var tmpFollows []model.Follow
			var txErr error
			txErr = tx.Model(&model.Follow{}).Where("uid = ?", uid).Find(&tmpFollows).Error
			if txErr != nil {
				return txErr
			}

			list = tmpFollows
			return nil
		})
	})
	return
}

func (service *ServiceUser) NumRelations(ctx context.Context, uids []int64) (results []*model.NumRelation, err error) {
	clearUids := util.UnionList(uids)
	var postCount = make(map[int64]int)
	var fansCount = make(map[int64]int)
	var followCount = make(map[int64]int)
	wg := &sync.WaitGroup{}

	wg.Add(1)
	go func() {
		defer wg.Done()
		response, tempErr := remoteRpc.GetPostRpc().PostRpcServiceClient.GetUserPostInfo(ctx, &postProto.UserPostInfoRequest{
			Ids: uids,
		})
		if tempErr == nil {
			if response.List != nil {
				for _, item := range response.List {
					postCount[item.Uid] = int(item.PostNum)
				}
			}
		}
	}()

	wg.Add(1)
	go func() {
		defer wg.Done()
		baseStore.GetDataStore().Execute(func(db *gorm.DB) {
			err = db.Transaction(func(tx *gorm.DB) error {
				for _, uid := range clearUids {
					var count int64
					txErr := tx.Model(&model.Fans{}).Where("uid = ?", uid).Count(&count).Error
					if txErr != nil {
						fansCount[uid] = 0
					} else {
						fansCount[uid] = int(count)
					}
				}
				return nil
			})
		})
	}()

	wg.Add(1)
	go func() {
		defer wg.Done()
		baseStore.GetDataStore().Execute(func(db *gorm.DB) {
			err = db.Transaction(func(tx *gorm.DB) error {
				for _, uid := range clearUids {
					var count int64
					txErr := tx.Model(&model.Follow{}).Where("uid = ?", uid).Count(&count).Error
					if txErr != nil {
						followCount[uid] = 0
					} else {
						followCount[uid] = int(count)
					}
				}
				return nil
			})
		})
	}()

	wg.Wait()

	for _, uid := range uids {
		item := &model.NumRelation{
			Uid:           uid,
			NumPost:       postCount[uid],
			NumFans:       fansCount[uid],
			NumFollowings: followCount[uid],
		}
		results = append(results, item)
	}

	return
}

func (service *ServiceUser) Relation(uid, targetUid int64) (relation string, err error) {
	baseStore.GetDataStore().Execute(func(db *gorm.DB) {
		err = db.Transaction(func(tx *gorm.DB) error {
			var oldFollow model.Follow
			var oldFans model.Fans
			hasFollow := false
			hasBeFollow := false
			tmperr := tx.Where("uid = ?", uid).Where("follow_uid = ?", targetUid).First(&oldFollow).Error
			if tmperr == nil {
				hasFollow = true
			}
			tmperr = tx.Where("uid = ?", uid).Where("fans_uid = ?", targetUid).First(&oldFans).Error
			if tmperr == nil {
				hasBeFollow = true
			}

			if hasFollow && hasBeFollow {
				relation = constants.MUTIL
			} else if hasFollow {
				relation = constants.FOLLOWING
			} else if hasBeFollow {
				relation = constants.BE_FOLLOWED
			} else {
				relation = constants.NO_RELATION
			}

			return nil
		})
	})
	return
}

func (service *ServiceUser) RelationMap(uid int64, targetUids []int64) (relation map[int64]string, err error) {
	baseStore.GetDataStore().Execute(func(db *gorm.DB) {
		err = db.Transaction(func(tx *gorm.DB) error {
			var follows []model.Follow
			var fans []model.Fans

			tmperr := tx.Where("uid = ?", uid).Find(&follows).Error
			if tmperr != nil {
				return tmperr
			}
			tmperr = tx.Where("uid = ?", uid).Find(&fans).Error
			if tmperr != nil {
				return tmperr
			}

			followMap := make(map[int64]int64)
			fansMap := make(map[int64]int64)
			for _, item := range follows {
				followMap[item.FollowUid] = item.FollowUid
			}

			for _, item := range fans {
				fansMap[item.FansUid] = item.FansUid
			}

			tmpRelationMap := make(map[int64]string)
			hasFollow := false
			hasBeFollow := false
			for _, targetUid := range targetUids {
				if _, ok := followMap[targetUid]; ok {
					hasFollow = true
				} else {
					hasFollow = false
				}

				if _, ok := fansMap[targetUid]; ok {
					hasBeFollow = true
				} else {
					hasBeFollow = false
				}

				if hasFollow && hasBeFollow {
					tmpRelationMap[targetUid] = constants.MUTIL
				} else if hasFollow {
					tmpRelationMap[targetUid] = constants.FOLLOWING
				} else if hasBeFollow {
					tmpRelationMap[targetUid] = constants.BE_FOLLOWED
				} else {
					tmpRelationMap[targetUid] = constants.NO_RELATION
				}
			}

			relation = tmpRelationMap
			return nil
		})
	})
	return
}

func (service *ServiceUser) FollowRecommend() (list []model.User, err error) {
	baseStore.GetDataStore().Execute(func(db *gorm.DB) {
		err = db.Transaction(func(tx *gorm.DB) error {
			var tmpList []model.User
			//Todo 推荐待优化
			tmperr := tx.Model(&model.User{}).Order("RAND()").Limit(10).Find(&tmpList).Error
			if tmperr != nil {
				return tmperr
			}
			list = tmpList
			return nil
		})
	})
	return
}
